<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>BVH labs</title>
<meta name="Description" content="" />
<meta name="Keywords"  content="" />
<link rel="icon" href="favicon.ico" />
<style>
html { width:100%; height:100%; }
*{ padding:0; margin:0; -o-user-select:none; -ms-user-select:none; -khtml-user-select:none; -webkit-user-select:none; -moz-user-select: none; text-align: center;}
body { background: #000; font:12px sans-serif; color:#fff; text-shadow:1px 1px 0px #000; overflow:hidden; }
#container { position:absolute; top:0; left:0; }
#guiContener { position:absolute; overflow-x:auto; height:100%; width:215px; top: 0; left: 0;}
#out2 { position:absolute; bottom:30px; right:6px; text-align: right; font-size:10px; pointer-events:none;}
#out1 { position:absolute; bottom:30px; left:6px; text-align: left; font-size:10px; pointer-events:none;}
#debug { position:absolute; top:6px; left:170px; text-align:left; pointer-events:none;}
#oimoMenu { position:absolute; top:0px; left:0px; }
#bvhMenu { position:absolute; top:100px; left:0px; }
#l1 { position:absolute; top:0px; left:0; width:160px; height:100%; background-color:rgba(30,30,40,0.4); pointer-events:none; border-right:1px solid #303040; }
#l2 { position:absolute; top:0px; right:0; width:160px; height:100%; background-color:rgba(30,30,40,0.4); pointer-events:none; border-left:1px solid #303040; }
#b0 { position:absolute; top:10px; left:5px; width:145px; height:30px; }
#b00 { position:absolute; top:50px; left:5px; width:145px; height:30px; }
#b1 { position:absolute; top:10px; left:5px; width:72px; height:30px; }
#b2 { position:absolute; top:10px; left:82px; width:72px; height:30px; }
#b3 { position:absolute; top:10px; right:5px; width:148px; height:30px; }
#b4 { position:absolute; top:50px; right:5px; width:148px; height:30px; }
#b5 { position:absolute; top:90px; right:5px; width:148px; height:30px; }
#b6 { position:absolute; top:130px; right:5px; width:148px; height:30px; }
#b7 { position:absolute; top:170px; right:5px; width:148px; height:30px; }

#dragZone { position:absolute; bottom:60px; left:5px; width:144px; height:45px; background-color:none;  border: 3px dashed #ccc; pointer-events:auto; }
#dragZone.hover { border: 3px dashed #333; }
#topfiles { position:absolute; bottom:5px; left:5px; width:144px; height:45px; background-color:none; pointer-events:none; border: 3px solid #ccc;}
#topfiles.hover { background-color:#999999; }
#files { position:absolute; bottom:5px; left:5px; width:150px; height:45px; opacity:0; }
#envSelector{position:absolute; bottom:10px; right:170px; border:3px solid #74818b; width:64px; height:64px; cursor:pointer;}
</style>
</head>
<body>
<script src="../src/three/three.js"></script>
<script src="../src/three/bvh.js"></script>
<script src="../src/ui/slider.js"></script>
<script src="../src/ui/navigation.js"></script>
<script src="../src/three/loader/SEA3D.js"></script>
<script src="../src/three/loader/SEA3DLoader.js"></script>
<script src="../src/three/loader/SEA3DLZMA.js"></script>

<script src="../src/ui/Transcode.js"></script>
<script src="../js/libs/oimo.min.js"></script>

<div id="viewport"></div>
<div id="l1"></div>
<div id="l2"></div>
<div id="debug"></div>
<input type="file" id="files" />

<div id="oimoMenu">
    <input type="submit" value="Physics on" id="b0">
    <input type="submit" value="BVH debug off" id="b00">
</div>

<div id="bvhMenu">
	<input type="submit" value="action" id="b1">
	<input type="submit" value="story" id="b2">
</div>

<input type="submit" value="show model" id="b3">
<input type="submit" value="Stick model" id="b4">
<input type="submit" value="Onkba model" id="b5">
<input type="submit" value="Dianna model" id="b6">
<input type="submit" value="Woman model" id="b7">

<div id="BVHhubs"></div>
<div id="out1"></div>
<div id="out2"></div>

<div id="dragZone"><br>Drag File</div>
<div id="topfiles"><br>Load File</div>
<div id="envSelector"></div>

<script type="text/javascript">

var doc = document;
var bodyLink = "../models/stickman.sea";

var out1, out2, debug, fileButton, fileSel, holder;
var environment, envcontext, envLists, nEnv = 0;
var camera, container, scene, renderer, delta, center, centerLight;
var ToRad = Math.PI / 180;
var ToDeg = 180 / Math.PI;
var hero = null;
var hero2 = null;
var oimoMenu, bvhMenu, s1, s2;
var buffgeoBox, buffgeoSphere;

var bvh;
var BVHset = {ax:"x", ay:"y", az:"z", dx:1, dy:1, dz:1, rx:0, ry:0, rz:0, order:"XYZ"};
var BVHanimConfig = { debug:true, speed:0.5, size:1, px:0, py:0, pz:0, boneSize:0.5 };

var bones = null;
var boneInverses = null;
var isViewModel = false;
var currentNames = null;
var currentDistance = null;

//oimo var
var world = null;
var oimoStep = null;
var collisionGroupes = {};
var bodysquel = null;
var meshsquel = null;
var meshs = null;
var bodys = null;
var isWithPhysics = false;
var isWithSqueleton = false;
var isWithObjects = false;

var isWithBVHSqueleton = true;

window.onload = initInterface;

function initInterface() {
	
	out1 = doc.getElementById("ou1");
    out2 = doc.getElementById("out2");
    debug = doc.getElementById("debug");
    fileButton = doc.getElementById('files');
    fileSel = doc.getElementById('topfiles');
	holder = doc.getElementById('dragZone');
	container = doc.getElementById("viewport");
	bvhMenu = doc.getElementById("bvhMenu");
	oimoMenu = doc.getElementById("oimoMenu");

	holder.ondragover = function () { this.className = 'hover'; return false; };
	holder.ondragend = function () { this.className = ''; return false; };
	holder.ondrop = function (e) {
		this.className = '';
		e.preventDefault();
		var file = e.dataTransfer.files[0];
		var reader = new FileReader();
		holder.innerHTML = '<br>'+ file.name;
	    reader.onload = (function(theFile) { return function(e) { bvh.parseData(e.target.result.split(/\s+/g));}; })(file);
	    reader.readAsText(file);
	    return false;
	};
    fileButton.addEventListener('change', handleFileSelect, false);
    fileButton.onmouseover = function () { fileSel.className = 'hover'; return false; };
    fileButton.onmouseout = function () { fileSel.className = ''; return false; };

    doc.getElementById("b0").addEventListener( 'click', function ( e ) { physicsOnOff(); }, false );
    doc.getElementById("b00").addEventListener( 'click', function ( e ) { bvhOnOff(); }, false );

    doc.getElementById("b1").addEventListener( 'click', function ( e ) { loadBVH("../images/bvh/action.png"); }, false );
    doc.getElementById("b2").addEventListener( 'click', function ( e ) { loadBVH("../images/bvh/story.png"); }, false );   

    doc.getElementById("b3").addEventListener( 'click', function ( e ) {heroVisibility(); }, false );
    doc.getElementById("b4").addEventListener( 'click', function ( e ) { loadSea3dBody("../models/stickman.sea"); }, false );
    doc.getElementById("b5").addEventListener( 'click', function ( e ) { loadSea3dBody("../models/onkba.sea"); }, false );
    doc.getElementById("b6").addEventListener( 'click', function ( e ) { loadSea3dBody("../models/dianna.sea"); }, false );
    doc.getElementById("b7").addEventListener( 'click', function ( e ) { loadSea3dBody("../models/woman.sea"); }, false );

    s1 = new UI.Slide(bvhMenu, "speed", setSpeed, 50, [5,70, 150, 20]);
    s2 = new UI.Slide(bvhMenu, "bones", setBones, 25, [5,110, 150, 20]);
	
	init3D();
    animate();
}

function bvhOnOff(evt) {
	if(isWithBVHSqueleton){ bvh.showHideSkeleton(false); isWithBVHSqueleton=false; doc.getElementById("b00").value = "BVH debug on";}
	else { bvh.showHideSkeleton(true); isWithBVHSqueleton=true; doc.getElementById("b00").value = "BVH debug off";}
}

function physicsOnOff(evt) {
	if(isWithPhysics){ removePhysics(); doc.getElementById("b0").value = "Physics on";}
	else { initOimoPhysics(); doc.getElementById("b0").value = "Physics off";}
}

function handleFileSelect(evt) {
    var f = evt.target.files[0];
	var reader = new FileReader();
	reader.onload = (function(theFile) { return function(e) { bvh.parseData(e.target.result.split(/\s+/g));}; })(f);
	reader.readAsText(f);
}

function debugTell(s) {
    debug.innerHTML = s;
}

function init3D() {
    
    initEnvironment();

	scene = new THREE.Scene();
	camera = new THREE.PerspectiveCamera( 45,  window.innerWidth / window.innerHeight, 1, 2000 );
    initCamera(120,70,200);
    center = new THREE.Vector3(0,40,0);
	centerLight =  new THREE.Vector3(0,-45,0);
	moveCamera();

    var backGeo = new THREE.BufferGeometry().fromGeometry( new THREE.IcosahedronGeometry(300,1) );
    var backMat = new THREE.MeshBasicMaterial( { map:gradTexture([[0.75,0.5,0.45, 0.2], ['#808080','#2e3032','#09050e', '#86a4bc']]), side:THREE.BackSide, depthWrite:false });
    var back = new THREE.Mesh( backGeo, backMat );
    back.geometry.applyMatrix(new THREE.Matrix4().makeRotationZ(5*ToRad));
    scene.add( back );
    
    var groundMaterial = new THREE.MeshBasicMaterial( { color: 0xFFFFFF, transparent: true,  depthWrite: false } );
	groundMaterial.blending = THREE[ "MultiplyBlending" ];
	var ground = new THREE.Mesh(new THREE.PlaneBufferGeometry( 1000, 1000, 4, 4 ), groundMaterial);
	ground.position.set( 0, 5, 0 );
	ground.rotation.x = - Math.PI / 2;
	//ground.receiveShadow = true;
	scene.add( ground );

    buffgeoBox = new THREE.BufferGeometry().fromGeometry( new THREE.BoxGeometry( 1, 1, 1 ) );
    buffgeoSphere = new THREE.BufferGeometry().fromGeometry( new THREE.SphereGeometry( 1, 20, 10 ) );
    
    var helper = new THREE.GridHelper( 100, 50 );
	helper.setColors( 0x909090, 0x606060 );
	scene.add( helper );
    helper.position.y = 5;

    renderer = new THREE.WebGLRenderer({ precision: "mediump", antialias:true });
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.autoClear = false;
    //renderer.shadowMapEnabled = true;
    //renderer.gammaInput = true;
    //renderer.gammaOutput = true;

    container.appendChild( renderer.domElement );

    initEvents();
	initBVH();
	
	heroVisibility();
}

function animate() {
    requestAnimationFrame( animate );
    updateBVH();
    //renderer.clear();
    renderer.render( scene, camera );
}

//----------------------------------
// BVH READER
//----------------------------------

function initBVH() {
    bvh = new BVH.Reader();
    //bvh.reScale(0.5);
    loadBVH("../images/bvh/tpose.bvh");
    //loadBVH("bvh/story.png");
}

function updateBVH() {
    if(bvh !== null) bvh.update();
    if(isViewModel) updateSkinBones();
}

function setSpeed(v) {
	BVHanimConfig.speed = v/100;
	bvh.speed = BVHanimConfig.speed
}

function setBones(v) {
	var s = v/30;
	if(s>0) BVHanimConfig.boneSize = s;
	else BVHanimConfig.boneSize = 0.1;
	bvh.boneSize = BVHanimConfig.boneSize;
}

function loadBVH(file) {
    bvh.boneSize = BVHanimConfig.boneSize;
    bvh.speed = BVHanimConfig.speed;
    bvh.load(file);
    if(isWithPhysics) bvh.endFunction = createOimoSqueleton;
}

//----------------------------------
//  OIMO PHYSICS
//----------------------------------

function initOimoPhysics(){
	isWithPhysics = true;
    // The Bit of a collision group
    collisionGroupes = {
        group1 : 1 << 0,  // 00000000 00000000 00000000 00000001
        group2 : 1 << 1,  // 00000000 00000000 00000000 00000010
        group3 : 1 << 2,  // 00000000 00000000 00000000 00000100
        all : 0xffffffff  // 11111111 11111111 11111111 11111111
    }

    matSphere = new THREE.MeshBasicMaterial( { envMap:environment, map: basicTexture(0), name:'sph' } );
    matBox = new THREE.MeshBasicMaterial( { envMap:environment, map:basicTexture(2), name:'box' } );
    matSphereSleep = new THREE.MeshBasicMaterial( {envMap:environment, map: basicTexture(1), name:'ssph' } );
    matBoxSleep = new THREE.MeshBasicMaterial( {envMap:environment,  map: basicTexture(3), name:'sbox' } );

    world = new OIMO.World(1/60, 2, 8, true);
    world.gravity = new OIMO.Vec3(0, -1, 0);

    createOimoSqueleton();
    createOimoObject();

    oimoStep = setInterval(updateOimoPhysics, 1000/60);
}

function removePhysics(){
	isWithPhysics = false;
	if(isWithSqueleton)deleteOimoSkeleton();
	if(isWithObjects)deleteOimoObject();
	clearInterval(oimoStep);
	world.clear();
	
}

function createOimoSqueleton() {
    if(currentNames!==null) deleteOimoSkeleton();

    bodysquel = {};
    meshsquel = {};
    currentNames = [];
    currentDistance = [];

    var node, name;
    var w=6, h = 6;
    var distance = 2;
    var boneMaterial = new THREE.MeshBasicMaterial( { color: 0xFFFF33, transparent:true, opacity:0.1, depthTest: false, depthWrite: true } );
    var boneHeadMaterial = new THREE.MeshBasicMaterial( { color: 0x33FF33, transparent:true, opacity:0.1, depthTest: false, depthWrite: true } )

    var config = [10,0.4,0.2, collisionGroupes.group1, collisionGroupes.group2];

    var i = bvh.nodes.length;
    while(i--){
        node = bvh.nodes[i];
        name = node.name;
        distance = bvh.distances[name];
        if(name!=='Site'){
            /*if(bvh.ParentNodes[name] !== null){
                var p1 = node.position;//new THREE.Vector3().setFromMatrixPosition( node.matrixWorld )
                var p2 = bvh.ParentNodes[name].position;//new THREE.Vector3().setFromMatrixPosition( childNodes[name].matrixWorld );
                distance = DistanceTest(p1, p2);
            }*/
            /*if(bvh.ChildNodes[name] !== null){
                // get distance to child
                var p1 = new THREE.Vector3().setFromMatrixPosition( node.matrixWorld );
                var p2 = bvh.ChildNodes[name].position;
                //var p2 = new THREE.Vector3().setFromMatrixPosition( bvh.ChildNodes[name].matrix );
                distance = DistanceTest(p1, p2);
            } else {
                distance = 2;
            }*/
            currentNames.push(name);
            currentDistance[i] = distance;

            if(name=='Head'){
                bodysquel[name] = new OIMO.Body({type:'sphere', name:node.name, size:[w], move:true, noSleep:true, world:world, config:config});
                meshsquel[name] = new THREE.Mesh( buffgeoSphere, boneHeadMaterial );
                meshsquel[name].scale.set( w, w, w  );
            } else {
                bodysquel[name] = new OIMO.Body({type:'box', name:node.name, size:[w*2,h*2,distance], move:true, noSleep:true, world:world, config:config});
                meshsquel[name] = new THREE.Mesh( buffgeoBox, boneMaterial );
                meshsquel[name].scale.set( w, h, distance  );
            }
            //meshsquel[name].castShadow = true;
            //meshsquel[name].receiveShadow = true;
            scene.add( meshsquel[name] );
        }
    }
    isWithSqueleton = true;
}

function deleteOimoSkeleton() {
    isWithSqueleton = false;
    var i = currentNames.length;
    var name;
    while (i--){
        name = currentNames[i];
        scene.remove(meshsquel[name]);
        bodysquel[name].remove();
    }
}

function updateOimoPhysics() {

    world.step();

    // update squeleton physics object
    if(isWithSqueleton){
        var mtx = new THREE.Matrix4();
        var mtx2  = new THREE.Matrix4();
        var mtx3  = new THREE.Matrix4();
        var node, name;
        var pos = new THREE.Vector3(), quat = new THREE.Quaternion();
        var pos_base = new THREE.Vector3();
        var pos_target = new THREE.Vector3();

        var i = bvh.nodes.length;
        while(i--){
            node = bvh.nodes[i];
            name = node.name;
            if(name!=='Site'){
                mtx = new THREE.Matrix4();
                // get position of node
                pos_base.setFromMatrixPosition( node.matrixWorld );
                // apply position on matrix
                mtx.makeTranslation(pos_base.x, pos_base.y, pos_base.z);

                if( bvh.ParentNodes[name] !== null ){
                }
                if(bvh.ChildNodes[name] !== null){
                    // get orientation look at child node
                    pos_target.setFromMatrixPosition( bvh.ChildNodes[name].matrixWorld );
                    mtx3 = new THREE.Matrix4();
                    mtx3.lookAt( pos_target, pos_base, new THREE.Vector3(0, 0, 1) );
                    mtx.multiply( mtx3 );
                }

                // lock rotationZ
                mtx2 = new THREE.Matrix4();
                mtx2.makeRotationZ( 0 );
                mtx.multiply( mtx2 );

                // translate to middle of node
                mtx2 = new THREE.Matrix4();
                mtx2.makeTranslation( 0,0,currentDistance[i]*0.5);
                mtx.multiply( mtx2 );

                pos.setFromMatrixPosition( mtx );
                quat.setFromRotationMatrix( mtx );

                if(bodysquel[name]!==null && bodysquel[name]!==null){
                    bodysquel[name].setPosition(pos);
                    bodysquel[name].setQuaternion(quat);
                    meshsquel[name].position.copy(pos);
                    meshsquel[name].quaternion.copy(quat);
                }
            }
        }
    }

   // update dynamique object
   if(isWithObjects){
	    var x, y, z;
	    var i = bodys.length;
	    var mesh;
	    var body;

	    while (i--){
	        body = bodys[i];
	        mesh = meshs[i];

	        if(!body.getSleep()){

	            mesh.position.copy(body.getPosition());
	            mesh.quaternion.copy(body.getQuaternion());

	            // change material
	            if(mesh.material.name === 'sbox') mesh.material = matBox;
	            if(mesh.material.name === 'ssph') mesh.material = matSphere; 

	            // reset position
	            if(mesh.position.y<-100){
	                x = -100 + Math.random()*200;;
	                z = -100 + Math.random()*200;
	                y = 100 + Math.random()*500;
	                body.resetPosition(x,y,z);
	            }
	        } else {
	            if(mesh.material.name === 'box') mesh.material = matBoxSleep;
	            if(mesh.material.name === 'sph') mesh.material = matSphereSleep;
	        }
	    }
	}
}

function createOimoObject() {

	isWithObjects = true;

    // reset old
    bodys = [];
    meshs = [];

    var config = [0,0.4,0.2, collisionGroupes.group3, collisionGroupes.all & ~ collisionGroupes.group1];

    //add ground
    var ground = new OIMO.Body({size:[200, 40, 200], pos:[0,-15,0], world:world, config:config});
    // wall
    var w0 = new OIMO.Body({ type:"box", size:[200,500,50], pos:[0,250,-100], world:world, config:config });
    var w1 = new OIMO.Body({ type:"box", size:[200,500,50], pos:[0,250, 100], world:world, config:config });
    var w2 = new OIMO.Body({ type:"box", size:[50,500,200], pos:[-100,250,0], world:world, config:config });
    var w3 = new OIMO.Body({ type:"box", size:[50,500,200], pos:[100,250,0], world:world, config:config });

    config = [1,0.4,0.2, collisionGroupes.group2, collisionGroupes.all];

    // now add object
    var x, y, z, w, h, d;
    var i = 200;

    while (i--){
        t = Math.floor(Math.random()*2)+1;
        x = -100 + Math.random()*200;;
        z = -100 + Math.random()*200;
        y = 100 + Math.random()*500;
        w = 10 + Math.random()*10;
        h = 5 + Math.random()*10;
        d = 5 + Math.random()*10;

        if(t===1){       
            bodys[i] = new OIMO.Body({type:'sphere', size:[w*0.5], pos:[x,y,z], move:true, world:world, config:config});
            meshs[i] = new THREE.Mesh( buffgeoSphere, matSphere );
            meshs[i].scale.set( w*0.5, w*0.5, w*0.5 );
        } else if(t===2){
           
            bodys[i] = new OIMO.Body({type:'box', size:[w,h,d], pos:[x,y,z], move:true, world:world, config:config});
            meshs[i] = new THREE.Mesh( buffgeoBox, matBox );
            meshs[i].scale.set( w, h, d );
        }
        scene.add( meshs[i] );
    }
}

function deleteOimoObject() {
	isWithObjects = false;
	var i = bodys.length;
	while (i--){
		bodys[i].remove();
		scene.remove( meshs[i] );
	}
}

//----------------------------------
// TODO SEA3D TEST 
//----------------------------------

var origine ={};
var skeletonHelper;

function removeHero(){
    isViewModel = false;
    scene.remove(hero);
    hero.material.dispose();
    hero.geometry.dispose();
}

function loadSea3dBody(url){
    if(isViewModel) removeHero();
    var bodyName;
    var size = 1;
    var loader = new THREE.SEA3D( false );
	loader.invertZ = true;
    loader.flipZ = true;
    loader.onComplete = function( e ) {
        if(url == '../models/stickman.sea')bodyName="body";
        if(url == '../models/onkba.sea')bodyName="onkba";
        if(url == "../models/woman.sea")bodyName="womBody";
        if(url == '../models/dianna.sea')bodyName="dianna_b";

        hero = loader.getMesh(bodyName).clone();

        hero.material.skinning = true;
        hero.material.envMap = environment;
        hero.material.transparent = true;
        hero.material.normalScale={x:-2,y:-2};
        hero.material.color = new THREE.Color( 0xFFFFFF );
        hero.material.ambient = new THREE.Color( 0xFFFFFF );
        hero.material.specular = new THREE.Color( 0xFFFFFF );
        hero.material.emissive = new THREE.Color( 0xFFFFFF );
        hero.material.shininess = 10;

		hero.position.set(0,0,0);
		hero.rotation.set(0,0,0);
		hero.scale.set(size,size,size);

        scene.add(hero);

        //skeletonHelper = new THREE.SkeletonHelper( hero );
        //skeletonHelper.material.linewidth = 3;
        //scene.add( skeletonHelper );
        //scene.add(g);
        //g.scale.set(size,size,size);

       
       /* hero2 = loader.getMesh(bodyName).clone();
        hero2.material = shaders[0];
        hero2.position.set(100,0,0);
        hero2.rotation.set(0,0,0);
        hero2.scale.set(size,size,-size);
        scene.add(hero2);*/
        

        bones = hero.skeleton.bones;
        //skeletonHelper.bones = hero.skeleton.bones;
        boneInverses = hero.skeleton.boneInverses;

        
        var i = hero.skeleton.bones.length;
        //
        while(i--){
            origine[hero.skeleton.bones[i].name]=hero.skeleton.bones[i].matrix;
        }
        
        hero.visible = true;
        isViewModel = true;
		
        doc.getElementById("b3").value = "hide model";
    }
    loader.parser = THREE.SEA3D.AUTO;
    loader.load( url );
}

function heroVisibility() {
    if(hero == null){ loadSea3dBody(bodyLink); return;}
    if(isViewModel){isViewModel = false; hero.visible=false; doc.getElementById("b3").value = "show model";}
    else{ isViewModel = true; hero.visible=true; doc.getElementById("b3").value = "hide model";}
}

var RAD = Math.PI / 180;
var DEG = 180 / Math.PI;

//var nameReplace = {};
//nameReplace["RightLowLeg"] = "LeftLowLeg";
//nameReplace["LeftLowLeg"] = "RightLowLeg";

THREE.Skeleton.prototype.update = ( function () {

	var offsetMatrix = new THREE.Matrix4();
	
	return function () {

		// flatten bone matrices to array

		//for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
		var b =  this.bones.length;
		while(b--){

			// compute the offset between the current and the original transform
            if(!this.bones[ b ].mtx ) this.bones[ b ].mtx = this.bones[ b ].matrixWorld; 

			var matrix = this.bones[ b ] ? this.bones[ b ].mtx : this.identityMatrix;
            //var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix;

			offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] );
			//offsetMatrix.multiplyMatrices(  this.boneInverses[ b ], matrix );
			offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );

		}

		if ( this.useVertexTexture ) {
			this.boneTexture.needsUpdate = true;
		}
		
	};

} )();

var preservesBoneSize = true;

function updateSkinBones() {
    var bone, node, name;
	var nodes = bvh.Nodes;
	var len = bones.length;
	var notAsign = [];

    var globalPos = new THREE.Vector3();
    var globalQuat = new THREE.Quaternion();
    var globalMtx = new THREE.Matrix4();
	
    //for(var i = 0; i < len; i++)
	//for(var i = len-1; i > -1; i--)
	var i =  len-1;
    while(i--)
	{	
        bone = bones[i];
        name = bone.name;
		
		//if (nameReplace[name]) name = nameReplace[name];
		//if(name=='root')return;
        if ( node = nodes[name] ){
        	//if(name!=='LeftCollar' && name!=='RightCollar'){
        		//console.log(name)
        	//}
           

            /*var originePos = new THREE.Vector3();
            var origineQuat = new THREE.Quaternion()
            var origineScale =  new THREE.Vector3();
            if(origine[name]){
                origine[name].decompose( originePos, origineQuat, origineScale );
            }*/


			//
			// LOCAL TO GLOBAL
			//

            var nodeMtx = new THREE.Matrix4().copy( node.matrixWorld );
            //var nodeMtx = new THREE.Matrix4().copy( node.matrix );

            /*
            var pos_base = new THREE.Vector3().setFromMatrixPosition( node.matrixWorld );
			var mtx = new THREE.Matrix4();
            // apply position on matrix
            mtx.makeTranslation(pos_base.x, pos_base.y, pos_base.z);
*/ 
            var pos_target = new THREE.Vector3()
			
            //if(bvh.ChildNodes[name]){
                 //var ttnodeMtx = new THREE.Matrix4().copy( bvh.ChildNodes[name].matrixWorld );
                
               //pos_target.setFromMatrixPosition( bvh.ChildNodes[name].matrixWorld );
                //var mtx3 = new THREE.Matrix4();
                ///mtx3.lookAt( pos_target, pos_base, new THREE.Vector3(0, 0, 1) );
                //mtx.multiply( mtx3 );
            //}
            //if( bvh.ParentNodes[name]){
                //pos_target.setFromMatrixPosition( bvh.ParentNodes[name].matrixWorld );
            //}
			globalPos.setFromMatrixPosition( nodeMtx );
             
            //globalPos.add(originePos)
            globalQuat.setFromRotationMatrix( nodeMtx );
            //var globalEuler = new THREE.Euler().setFromRotationMatrix( nodeMtx );
           // var globalPos = new THREE.Vector3().setFromMatrixPosition( mtx );
           // var globalQuat = new THREE.Quaternion().setFromRotationMatrix( mtx );
            // rescale model
           // var globalScale = new THREE.Vector3(0.98,0.98,0.98) //origineScale;//new THREE.Vector3(2,2,2)
           // var globalPos2 = new THREE.Vector3().multiplyVectors(globalPos, globalScale);
			
			//
			//	PREPARES MATRIX
			//
			
			var globalMtx = new THREE.Matrix4();	

			if (!bone.rootMatrix){
				bone.rootMatrix = bone.matrixWorld.clone();		
			}
			
			//
			//	MODIFY TRANSFORM
			//
			//globalMtx.setPosition( globalPos );
			//globalMtx.makeRotationFromEuler( globalEuler );
            globalMtx.makeRotationFromQuaternion( globalQuat );
            //globalMtx.makeRotationFromQuaternion( globalQuat );
			globalMtx.multiply( bone.rootMatrix );
            //globalMtx.makeTranslation(0, globalPos.y, globalPos.z);
            			
			globalMtx.setPosition( globalPos );	
            //globalMtx.scale(globalScale);
					
			//
			//	GLOBAL TO LOCAL
			//
			
			var inv = new THREE.Matrix4().getInverse( bone.parent.matrixWorld );
			//if(name=='LeftCollar' || name=='RightCollar') inv = new THREE.Matrix4();
			var localMtx = new THREE.Matrix4().multiplyMatrices( inv, globalMtx );
			
			globalMtx.multiplyMatrices( bone.parent.matrixWorld, localMtx );
			
			//	UPDATE BONE
			
			//bone.matrixAutoUpdate = false;
			//bone.matrix.copy(localMtx);
			//bone.matrixWorldNeedsUpdate = false;
			//bone.updateMatrixWorld(true);
			//bone.matrixWorld.copy(globalMtx.clone());
            //bone.updateMatrixWorld(true);
            //bone.position.copy(new THREE.Vector3().setFromMatrixPosition( globalMtx.clone() ));
            //bone.quaternion.copy(new THREE.Quaternion().setFromRotationMatrix( globalMtx.clone() ));
            //bone.matrixWorldNeedsUpdate = true;
			bone.mtx = globalMtx.clone();
		    //}
        } else {
			var globalMtx = new THREE.Matrix4();	
			globalMtx.multiplyMatrices( bone.parent.mtx ? bone.parent.mtx : bone.parent.matrixWorld, bone.matrix );
			
			bone.mtx = globalMtx;
            //bone.position.copy(new THREE.Vector3().setFromMatrixPosition( globalMtx ));
            //bone.quaternion.copy(new THREE.Quaternion().setFromRotationMatrix( globalMtx ));
            //bone.matrixWorldNeedsUpdate = true;
		}
    }

    
	
	if (preservesBoneSize)
	{
		for(var i = 0; i < len; i++)
		{	
			bone = bones[i];
			name = bone.name;
           // return;
			if(name!=='Hips'){// && name!=='LeftCollar' && name!=='RightCollar'){
    			var parentMtx = bone.parent.mtx ? bone.parent.mtx : bone.parent.matrixWorld;
    			
    			if (node = nodes[name]){
    				//
    				//	GLOBAL TO LOCAL
    				//								
    				var origineInv = new THREE.Matrix4();
    				var originePos = new THREE.Vector3();
    				//var inv = new THREE.Matrix4();
    				//inv.getInverse( parentMtx );
                    origineInv.getInverse( parentMtx );
    				
    				var localMtx = new THREE.Matrix4();				
    				//localMtx.multiplyMatrices( inv, bone.mtx );	
                    localMtx.multiplyMatrices( origineInv, bone.mtx );
                    //localMtx.multiplyMatrices( bone.mtx, origineInv );		
    								
    				//--
    				
    				//var bonePos = new THREE.Vector3();
    				//bonePos.setFromMatrixPosition( bone.matrix );

                    originePos.setFromMatrixPosition( bone.matrix );
                    //originePos.setFromMatrixPosition( bone.matrixWorld );
                    //originePos.setFromMatrixPosition( bone.skinmatrix );
    				
    				//localMtx.setPosition( bonePos );
                    localMtx.setPosition( originePos );						
    				
    				//--
    				
    				var globalMtx = new THREE.Matrix4();
    				globalMtx.multiplyMatrices( parentMtx, localMtx );
    			
    				bone.mtx = globalMtx;				
    			} else {
    				var globalMtx = new THREE.Matrix4();	
    				globalMtx.multiplyMatrices( parentMtx, bone.matrix );
    				bone.mtx = globalMtx;
    			}
            }
		}
	}
    //skeletonHelper.bones = bones;//hero.skeleton.bones;
    //skeletonHelper.update();
}

//----------------------------------
//  AUTO TEXTURE
//----------------------------------

function gradTexture(color) {
    var c = doc.createElement("canvas");
    var ct = c.getContext("2d");
    c.width = 16; c.height = 256;
    var gradient = ct.createLinearGradient(0,0,0,256);
    var i = color[0].length;
    while(i--){ gradient.addColorStop(color[0][i],color[1][i]); }
    ct.fillStyle = gradient;
    ct.fillRect(0,0,16,256);
    var texture = new THREE.Texture(c);
    texture.needsUpdate = true;
    return texture;
}

function basicTexture(n){

    var canvas = doc.createElement( 'canvas' );
    canvas.width = canvas.height = 64;
    var ctx = canvas.getContext( '2d' );
    var colors = [];
    if(n===0){ // sphere
        colors[0] = "#58AA80";
        colors[1] = "#58FFAA";
    }
    if(n===1){ // sphere sleep
        colors[0] = "#383838";
        colors[1] = "#38AA80";
    }
    if(n===2){ // box
        colors[0] = "#AA8058";
        colors[1] = "#FFAA58";
    }
    if(n===3){ // box sleep
        colors[0] = "#383838";
        colors[1] = "#AA8038";
    }
    ctx.fillStyle = colors[0];
    ctx.fillRect(0, 0, 64, 64);
    ctx.fillStyle = colors[1];
    ctx.fillRect(0, 0, 32, 32);
    ctx.fillRect(32, 32, 32, 32);

    var tx = new THREE.Texture(canvas);
    tx.needsUpdate = true;
    return tx;
}

//----------------------------------
//  ENVIRONEMENT
//----------------------------------

function initEnvironment(){
	environment = THREE.ImageUtils.loadTexture( '../images/spherical/env.jpg', THREE.SphericalReflectionMapping );
    envLists = ['env.jpg','env1.jpg','env2.jpg','env3.jpg','env4.jpg','env5.jpg','env6.jpg','env7.jpg','env8.jpg','env9.jpg','env10.jpg'];
    var env = document.getElementById('envSelector');
    var canvas = document.createElement( 'canvas' );
    canvas.width = canvas.height = 64;
    env.appendChild( canvas );
    envcontext = canvas.getContext('2d');
    loadNewEnv(true);
    env.onclick = loadNewEnv;
} 

function loadNewEnv(){
    var img = new Image();
    img.onload = function(){
        nEnv++;
        if(nEnv==envLists.length) nEnv = 0;
        envcontext.drawImage(img, 0, 0, 64,64);
        environment.image = img;
        environment.needsUpdate = true;
    }
    img.src = '../images/spherical/'+envLists[nEnv];
}

</script>
</body>
</html>
